using DTT.AreaOfEffectRegions;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Logic;
using LFloatMath.Collision2D;
using LFloatMath.Math;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class BattleRenderCollisionController : BattleRenderBaseController
    {
        private BetterLinkedList<CollisionViewComponent> collisionComponents = new BetterLinkedList<CollisionViewComponent>();
        private Dictionary<uint, HashSet<CollisionViewComponent>> onHit = new Dictionary<uint, HashSet<CollisionViewComponent>>();

        private Dictionary<uint, (ColliderProxy, Predicate<CollisionViewComponent>, UnitView)> tempProxys = new Dictionary<uint, (ColliderProxy, Predicate<CollisionViewComponent>, UnitView)>();
        // private ColliderProxy tempProxy;
        // private ECollisionType tempType = ECollisionType.None;
        // private bool dirty = false;
        private HashSet<uint> dirtyCollision = new HashSet<uint>();
        private uint tempProxyID = 1;

        protected override void OnUpdate(float deltaTime)
        {
            base.OnUpdate(deltaTime);
            this.CheckCollision();
            // 这里集中设置选中
            if (this.collisionComponents.Count > 0)
            {
                var rNode = this.collisionComponents.FirstNode;
                while (rNode != null)
                {
                    rNode.value.unit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent).selectedCollision = -1;
                    rNode = rNode.next;
                }
            }
            foreach (var rPair in this.onHit)
            {
                if (this.tempProxys.TryGetValue(rPair.Key, out var rTempProxyData))
                {
                    foreach (var rCol in rPair.Value)
                    {
                        rCol.unit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent).selectedCollision = rTempProxyData.Item3.teamIndex;
                    }
                }
            }
        }

        public void AddCollider(CollisionViewComponent rColComp)
        {
            if (rColComp != null)
            {
                this.collisionComponents.AddLast(rColComp);
            }
        }

        public void RmoveCollider(CollisionViewComponent rColComp)
        {
            if (rColComp != null)
            {
                this.collisionComponents.Remove(rColComp);
            }
        }

        public uint ModifyTempCollision_Circle(UnitView unit, uint collisionID, LFloat fRadius, LVector2 rPos, Predicate<CollisionViewComponent> condition)
        {
            if (this.tempProxys.TryGetValue(collisionID, out var rItem))
            {
                var tempProxy = rItem.Item1;
                var rOldPrefab = tempProxy.Prefab.collider as CCircle;
                if (rOldPrefab?.radius == fRadius)
                {
                    var rOldPos = tempProxy.pos;
                    if (rOldPos != rPos || rItem.Item2 != condition)
                    {
                        tempProxy.pos = rPos;
                        this.tempProxys[collisionID] = (tempProxy, condition, unit);
                        this.dirtyCollision.Add(collisionID);
                    }
                }
            }
            else
            {
                this.RemoveTempCollision(collisionID);
                var tempProxy = ColliderProxy.Fetch();
                tempProxy.Init(CollisionUtil.GetCirclePrefab(fRadius), rPos);
                tempProxy.IsStatic = false;
                tempProxy.LayerType = 0;

                collisionID = ++this.tempProxyID;
                this.tempProxys[collisionID] = (tempProxy, condition, unit);
                this.dirtyCollision.Add(collisionID);
            }
            return collisionID;
        }

        public uint ModifyTempCollision_Cub(UnitView unit, uint collisionID, LFloat width, LFloat height, LVector2 aspect, LVector2 rPos, Predicate<CollisionViewComponent> condition)
        {
            var rAspect = aspect.normalized;
            //偏移中心到矩形中心
            rPos += height / 2 * rAspect;
            var fNewDeg = LMath.AngleIntUp(rAspect);
            if (this.tempProxys.TryGetValue(collisionID, out var rItem))
            {
                var tempProxy = rItem.Item1;
                var rOldPrefab = tempProxy.Prefab.collider as COBB;
                if (rOldPrefab?.size.x == width / 2 && rOldPrefab.size.y == height / 2)
                {
                    var rOldPos = tempProxy.pos;
                    var fOldDeg = tempProxy.deg;
                    if (rOldPos != rPos || fOldDeg != fNewDeg || rItem.Item2 != condition)
                    {
                        tempProxy.pos = rPos;
                        tempProxy.deg = fNewDeg;
                        this.tempProxys[collisionID] = (tempProxy, condition, unit);
                        this.dirtyCollision.Add(collisionID);
                    }
                    return collisionID;
                }
            }
            else
            {
                this.RemoveTempCollision(collisionID);
                // 获取OBB
                var tempProxy = ColliderProxy.Fetch();
                tempProxy.Init(CollisionUtil.GetCubePrefab(width / 2, height / 2, LVector2.up), rPos);
                tempProxy.IsStatic = false;
                tempProxy.LayerType = 0;
                tempProxy.deg = fNewDeg;

                collisionID = ++this.tempProxyID;
                this.tempProxys[collisionID] = (tempProxy, condition, unit);
                this.dirtyCollision.Add(collisionID);
            }
            return this.tempProxyID;
        }

        public uint ModifyTempCollision_Fan(UnitView unit, uint collisionID, LFloat fRadius, LFloat fAngle, LVector2 rForward, LVector2 rPos, Predicate<CollisionViewComponent> condition)
        {
            if (this.tempProxys.TryGetValue(collisionID, out var rItem))
            {
                var tempProxy = rItem.Item1;
                var rOldPrefab = tempProxy.Prefab.collider as CFan;
                if (rOldPrefab?.radius == fRadius)
                {
                    var rOldPos = tempProxy.pos;
                    var fNewDeg = LMath.AngleIntUp(rForward);
                    var fOldDeg = tempProxy.deg;
                    if (rOldPos != rPos || fOldDeg != fNewDeg || rItem.Item2 != condition)
                    {
                        tempProxy.pos = rPos;
                        tempProxy.deg = fNewDeg;
                        this.tempProxys[collisionID] = (tempProxy, condition, unit);
                        this.dirtyCollision.Add(collisionID);
                    }
                    return collisionID;
                }
            }
            else
            {
                this.RemoveTempCollision(collisionID);
                var nDeg = LMath.AngleIntUp(rForward);
                var tempProxy = ColliderProxy.Fetch();
                tempProxy.Init(CollisionUtil.GetFanPrefab(fRadius, fAngle, LVector2.up), rPos);
                tempProxy.IsStatic = false;
                tempProxy.LayerType = 0;
                tempProxy.deg = nDeg;

                collisionID = ++this.tempProxyID;
                this.tempProxys[collisionID] = (tempProxy, condition, unit);
                this.dirtyCollision.Add(collisionID);
            }
            return collisionID;
        }

        public void RemoveTempCollision(uint collisionID)
        {
            if (this.tempProxys.TryGetValue(collisionID, out var rItem))
            {
                this.tempProxys.Remove(collisionID);
                ColliderProxy.Free(rItem.Item1);
            }
            this.onHit.Remove(collisionID);
        }

        /// <summary>
        /// 根据指示器参数创建碰撞用于预览 unit是发起者
        /// </summary>
        public void CreateIndicatorCollision(UnitView unit, TargetSelectConfig rSelect, Vector3 rPos, Vector3 rAspect, BattleSceneEntityView indicator, Predicate<CollisionViewComponent> condition, ref uint collisionID)
        {
            if (rSelect == null || indicator == null)
            {
                return;
            }
            switch ((ETargetSelectType)rSelect.type)
            {
                case ETargetSelectType.Circle:
                {
                    if (indicator.comp is CircleRegion rIndicator)
                    {
                        // 输入偏移
                        collisionID = this.ModifyTempCollision_Circle(unit, collisionID, rIndicator.Radius.ToLFloat(), rPos.ToLVector2XZ(), condition);
                    }
                    break;
                }
                case ETargetSelectType.Fan:
                {
                    if (rSelect.customConfig is TargetSelect_Fan_ConfigData rConfig && indicator.comp is ArcRegion rIndicator)
                    {
                        collisionID = this.ModifyTempCollision_Fan(unit, collisionID, rIndicator.Radius.ToLFloat(), rIndicator.Arc.ToLFloat(),
                            rAspect.ToLVector2XZ(), rPos.ToLVector2XZ(), condition);
                    }
                    break;
                }
                case ETargetSelectType.Cub:
                {
                    if (rSelect.customConfig is TargetSelect_Cub_ConfigData rConfig && indicator.comp is LineRegion rIndicator)
                    {
                        collisionID = this.ModifyTempCollision_Cub(unit, collisionID, rIndicator.Width.ToLFloat(), rIndicator.Length.ToLFloat(),
                            rAspect.ToLVector2XZ(), rPos.ToLVector2XZ(), condition);
                    }
                    break;
                }
            }
        }

        public int GetHitUnitNum(uint collisionID)
        {
            if (this.onHit.TryGetValue(collisionID, out var rList))
            {
                return rList.Count;
            }
            return 0;
        }

        private void CheckCollision()
        {
            if (this.collisionComponents.Count <= 0)
            {
                this.onHit.Clear();
                return;
            }
            foreach (var rPair in this.tempProxys)
            {
                bool bProxyDirty = this.dirtyCollision.Contains(rPair.Key);
                var rNode = this.collisionComponents.FirstNode;
                while (rNode != null)
                {
                    var rComp = rNode.value;
                    if (rComp.dirty || bProxyDirty)
                    {
                        var condition = rPair.Value.Item2;
                        var a = rPair.Value.Item1;
                        var b = rComp.proxy;
                        bool isCollided = a != b && (condition?.Invoke(rComp) ?? true) && CollisionHelper.CheckCollision(a.Prefab, a.Transform2D, b.Prefab, b.Transform2D);
                        if (!this.onHit.TryGetValue(rPair.Key, out var rColSet))
                        {
                            rColSet = new HashSet<CollisionViewComponent>();
                            this.onHit[rPair.Key] = rColSet;
                        }
                        if (isCollided)
                        {
                            rColSet.Add(rComp);
                        }
                        else
                        {
                            rColSet.Remove(rComp);
                        }
                    }
                    rNode = rNode.next;
                }
            }
            {
                // 取消comp上的dirty标签
                var rNode = this.collisionComponents.FirstNode;
                while (rNode != null)
                {
                    rNode.value.dirty = false;
                    rNode = rNode.next;
                }
            }
        }
    }
}